using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace Qreed.Windows.Forms
{
    public class ShellTreeNode : TreeNode, IDisposable
    {
        private ShellFolder.ItemType _includeFlags = ShellFolder.ItemType.Folder;

        /// <summary>
        /// Initializes a new instance of the <see cref="ShellTreeNode"/> class.
        /// </summary>
        internal ShellTreeNode(ShellFolder.ItemType includeFlags)
            : this(ShellPIDL.DesktopPIDL, includeFlags)
        {
            // Grab the desktop's IShellFolder
            
            // _shellFolder = ShellPIDL.DesktopShellFolder;

            // Get the PIDL for the desktop

            /*_shellPIDL = new ShellPIDL(Shell32.CSIDL.CSIDL_DESKTOP);

            Name = Text = _shellPIDL.DisplayName;
            ImageIndex = SelectedImageIndex = _shellPIDL.SelectedIconIndex;
            
            Nodes.Add("!!dummy!!");

            _includeOnlyFolders = includeOnlyFolders;*/
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ShellTreeNode"/> class.
        /// </summary>
        /// <param name="pidl">The pidl.</param>
        /// <param name="includeOnlyFolders">if set to <c>true</c> [include only folders].</param>
        private ShellTreeNode(ShellPIDL pidl, ShellFolder.ItemType includeFlags)
        {
            _shellPIDL = pidl;
            _includeFlags = includeFlags;

            Name = Text = _shellPIDL.DisplayName;
            ImageIndex = _shellPIDL.SelectedIconIndex;
            SelectedImageIndex = _shellPIDL.SelectedIconIndex;

            if (_shellPIDL.HasSubFolders)
                Nodes.Add("!!dummy!!");
        }
#if OLD 
        /*/// <summary>
        /// Initializes a new instance of the <see cref="ShellTreeNode"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="pIDL">The pIDL for this object.</param>
        private ShellTreeNode(ShellTreeNode parent, IntPtr pIDL, bool includeOnlyFolders)
        {
            _includeOnlyFolders = includeOnlyFolders;
            Construct(parent, pIDL);
        }*/

        private bool _includeOnlyFolders;

        private void Construct(ShellTreeNode parent, IntPtr pIDL)
        {
            if (parent == null)
                throw (new ArgumentNullException("parent"));

            if (pIDL == IntPtr.Zero)
                throw (new ArgumentNullException("pIDL"));

            _shellPIDL = new ShellPIDL(Shell32.ILCombine(parent.ShellPIDL.PIDL, pIDL));

            Name = Text = _shellPIDL.DisplayName;
            ImageIndex = _shellPIDL.IconIndex;
            SelectedImageIndex = _shellPIDL.SelectedIconIndex;

            if (_shellPIDL.IsFolder)
            {
                uint res = parent.ShellFolder.BindToObject(pIDL, IntPtr.Zero,
                                                           ref Shell32.IID_IShellFolder,
                                                           out _shellFolder);
                if (res != 0) Marshal.ThrowExceptionForHR((int)res);

                if (_shellPIDL.HasSubFolders)
                    Nodes.Add("!!dummy!!");
            }
        }
#endif
        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_shellPIDL != null)
                {
                    _shellPIDL.Dispose();
                    _shellPIDL = null;
                }
            }
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="ShellTreeNode"/> is reclaimed by garbage collection.
        /// </summary>
        ~ShellTreeNode()
        {
            Dispose(false);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the path.
        /// </summary>
        /// <value>The path.</value>
        public string Path
        {
            get {return _shellPIDL.Path;}
        }

        //private Shell32.IShellFolder _shellFolder;
        /// <summary>
        /// Gets the shell folder.
        /// </summary>
        /// <value>The shell folder.</value>
        //internal Shell32.IShellFolder ShellFolder
        //{
          //  get { return _shellFolder; }
        //}

        private ShellPIDL _shellPIDL;
        /// <summary>
        /// Gets the PIDL.
        /// </summary>
        /// <value>The PIDL.</value>
        public ShellPIDL ShellPIDL
        {
            get { return _shellPIDL; }
        }

        #endregion

        #region Methodes

        public void Refresh()
        {
            if (!this.IsExpanded)
            {
                // No use refreshing the desktop item if not expanded
                if (_shellPIDL.IsDesktop)
                    return;

                string path = Path;

                if (string.IsNullOrEmpty(path))
                {
                    // Cheap trick for system folder (like my computer)
                    PopulateSubNodes();
                }
                else
                {
                    _shellPIDL.Dispose();
                    _shellPIDL = ShellPIDL.FromPath(path);

                    Nodes.Clear();

                    if (_shellPIDL.HasSubFolders)
                        Nodes.Add("!!dummy!!");
                }
            }
            else
            {
                PopulateSubNodes();
            }
        }

        internal void PopulateSubNodes()
        {
            Nodes.Clear();

            if (_shellPIDL.HasSubFolders)
            {
                ShellPIDL[] children = _shellPIDL.ShellFolder.GetChildren(_includeFlags);

                foreach (ShellPIDL child in children)
                {
                    if (!Nodes.ContainsKey(child.DisplayName))
                    {
                        ShellTreeNode node = new ShellTreeNode(child, _includeFlags);
                        Nodes.Add(node);
                    }
                }
            }
        }        

#if OLD
        internal void PopulateSubNodes()
        {
            Nodes.Clear();

            Shell32.IEnumIDList enumList = null;

            try
            {
                Shell32.SHCONTF flags = Shell32.SHCONTF.SHCONTF_FOLDERS;

                if (!_includeOnlyFolders)
                    flags |= Shell32.SHCONTF.SHCONTF_NONFOLDERS;

                uint res = _shellFolder.EnumObjects(IntPtr.Zero, flags, out enumList);
                if (res != 0) Marshal.ThrowExceptionForHR((int)res);

                if(enumList == null)
                    return;
                IntPtr pIDL = IntPtr.Zero;
                int fetched = 0;

                try
                {
                    enumList.Next(1, out pIDL, out fetched);

                    while (pIDL != IntPtr.Zero && fetched > 0)
                    {
                        // Create a new child object
                        ShellTreeNode node = new ShellTreeNode(this, pIDL, _includeOnlyFolders);

                        if(!Nodes.Contains(node))
                            Nodes.Add(node);

                        Marshal.FreeCoTaskMem(pIDL);
                        pIDL = IntPtr.Zero;
                        fetched = 0;

                        enumList.Next(1, out pIDL, out fetched);
                    }
                }
                finally
                {
                    if (pIDL != IntPtr.Zero) Marshal.FreeCoTaskMem(pIDL);
                }
            }
            finally
            {
                if (enumList != null) Marshal.ReleaseComObject(enumList);
            }
        }        
#endif

        #endregion

    }
}
